package com.administrator.scoreManageSys.entity.shiro;

import com.administrator.scoreManageSys.utils.JwtTokenUtils;
import com.administrator.scoreManageSys.utils.RedisUtils;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.HashSet;
import java.util.Set;

import static com.administrator.scoreManageSys.system.ReturnMsg.NOT_ROLE_AND_PERMISSION_EXC;
import static com.administrator.scoreManageSys.system.SystemConstant.ROLE_PERMISSION;

/**
 * 基于JWT（ JSON WEB TOKEN）的认证域
 *
 * @author Administrator
 */
@Component
public class JwtRealm extends AuthorizingRealm {


    /**
     * redis 操作模板
     */
    @Resource
    private RedisUtils redisUtils;

    @Override
    public Class<?> getAuthenticationTokenClass() {
        //此Realm只支持JwtToken
        return JwtToken.class;
    }

    /**
     * 授权
     *
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        try {
            String jwtToken = (String) principals.getPrimaryPrincipal();
            // 从token中获取当用户的角色 Id
            Long roleId = JwtTokenUtils.getUserRoleIdFromToken(jwtToken);
            //设置角色权限
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            //从redis中获取角色的相关权限
            JSONObject jsonObject = redisUtils.getRoles(roleId);
            if (jsonObject != null) {
                //获取权限
                JSONArray rolePermissions = jsonObject.getJSONArray(ROLE_PERMISSION);
                //shiro roles
                Set<String> roles = new HashSet<>();
                //shiro permissions
                Set<String> permissions = new HashSet<>();
                //该用户所有角色权限设置
                for (int i = 0; i < rolePermissions.size(); i++) {
                    JSONObject role = rolePermissions.getJSONObject(i);
                    roles.add(role.getString("role"));
                    JSONArray permission = role.getJSONArray("permission");
                    for (int j = 0; j < permission.size(); j++) {
                        permissions.add(permission.getString(j));
                    }
                }
                info.setRoles(roles);
                info.setStringPermissions(permissions);
            } else {
                throw new UnauthorizedException(NOT_ROLE_AND_PERMISSION_EXC.toString());
            }
            return info;
        } catch (Exception e) {
            e.printStackTrace();
            throw new UnauthorizedException(NOT_ROLE_AND_PERMISSION_EXC.toString());
        }
    }


    /**
     * 认证
     *
     * @param token 用户认证token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        JwtToken jwtToken = (JwtToken) token;
        //真正的jwtToken
        String jwt = (String) jwtToken.getPrincipal();
        try {
            //获取当前用户Id
            Long userId = Long.valueOf(JwtTokenUtils.getUserIdFromToken(jwt));
            //获取当前用户登录后的token
            String redisToke = redisUtils.getToken(userId);
            //判断该token在redis中是否存在，如果存在则说明该token是合法
            if (redisToke != null) {
                //判断redis中是否存在该token
                if (redisToke.equals(jwt)) {
                    return new SimpleAuthenticationInfo(jwt, Boolean.TRUE, getName());
                }
            }
            throw new RuntimeException("登录失效，请重新登录！");
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("登录失效，请重新登录！");
        }
    }
}
